package parsers

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/micro-plat/lib4go/types"
)

// Parse 结构体，用于存储解析所需的正则表达式
type Parse struct {
	allowHostRegexps []*regexp.Regexp // 允许的主机正则表达式列表
	denyUrlRegexps   []*regexp.Regexp // 拒绝的URL正则表达式
	errorCodeRegexps *regexp.Regexp   // 允许的状态码正则表达式
}

// LogEntry 结构体，用于表示日志条目
type LogEntry struct {
	Host          string  `json:"host"`            // 主机名
	RemoteAddr    string  `json:"remote_addr"`     // 远程地址（这里可能是URL，根据实际情况调整）
	Status        int     `json:"status"`          // HTTP状态码
	RequestTime   float64 `json:"request_time"`    // 请求时间
	IsErrorStatus bool    `json:"is_error_status"` // 请求时间
}

// NewParse 函数，用于创建新的Parse实例
func NewParse(allowHosts ...string) *Parse {
	parse := &Parse{
		denyUrlRegexps: []*regexp.Regexp{regexp.MustCompile(`^.*\.[\w]{1,5}$`),
			regexp.MustCompile(`^/cgi-bin/.*`)}, // 拒绝以任意字符开头，后跟点号和3-5个字母或数字的URL
		errorCodeRegexps: regexp.MustCompile(`^[5-9][\d]{2,3}$`), // 允许任何有效的HTTP状态码（三位数字，首位非1）
		allowHostRegexps: make([]*regexp.Regexp, 0, len(allowHosts)),
	}
	// 遍历允许的主机列表，为每个主机创建正则表达式
	for _, host := range allowHosts {
		pattern := regexp.QuoteMeta(host)                  // 对主机名进行转义，以处理特殊字符
		pattern = strings.ReplaceAll(pattern, "\\*", ".*") // 将星号替换为正则表达式中的任意字符匹配
		pattern = "^" + pattern + "$"                      // 添加正则表达式的开始和结束标记
		regexPattern, err := regexp.Compile(pattern)
		if err != nil {
			panic(err)
		}
		parse.allowHostRegexps = append(parse.allowHostRegexps, regexPattern)
	}
	return parse
}

// Check 函数，用于检查给定的主机、URL和状态码是否符合要求
func (p *Parse) isAllowHost(host string) bool {
	if host == "" {
		return false
	}
	// 遍历允许的主机正则表达式列表，检查是否有匹配的主机
	for _, regex := range p.allowHostRegexps {
		if regex.MatchString(host) {
			return true
		}
	}
	return false
}
func (p *Parse) isAllowUrl(url string) bool {
	if url == "" {
		return false
	}
	// 遍历允许的主机正则表达式列表，检查是否有匹配的主机
	for _, regex := range p.denyUrlRegexps {
		if regex.MatchString(url) {
			return false
		}
	}
	return true
}

// ParseLog 函数，用于解析日志行并生成LogEntry实例
func (p *Parse) ParseLog(logLine string) (LogEntry, error) {
	fields := strings.Split(logLine, "|") // 使用竖线分割日志行
	// 如果日志行为空或字段数量小于8，则返回错误
	if logLine == "" || len(fields) < 8 {
		return LogEntry{}, fmt.Errorf("日志格式有误：%s", logLine)
	}

	// 提取主机信息
	hostField := strings.Fields(fields[7])[0]
	hostParts := strings.Split(strings.TrimSpace(hostField), ":")
	host := types.GetStringByIndex(hostParts, 0)
	if !p.isAllowHost(host) {
		return LogEntry{}, nil // host 不被允许，不返回错误但返回空的LogEntry
	}

	// 提取URL
	url := types.GetStringByIndex(strings.Split(fields[5], " "), 1)
	urlPath := strings.Split(url, "?")[0]
	// 如果URL被拒绝，则直接返回空LogEntry和nil错误（这里可能需要根据实际需求调整处理逻辑）
	if !p.isAllowUrl(urlPath) {
		return LogEntry{}, nil // URL被拒绝，不返回错误但返回空的LogEntry
	}

	// 转换状态码为整数
	status, err := strconv.Atoi(fields[1])
	if err != nil {
		return LogEntry{}, fmt.Errorf("状态码格式有误：%v, 日志：%s", err, logLine)
	}
	isErrorStatus := p.errorCodeRegexps.MatchString(fields[1])

	// 转换请求时间为浮点数
	requestTime, err := strconv.ParseFloat(fields[4], 64)
	if err != nil {
		return LogEntry{}, fmt.Errorf("请求时间格式有误：%v, 日志：%s", err, logLine)
	}

	// 创建LogEntry实例
	logEntry := LogEntry{
		Host:          host,
		RemoteAddr:    urlPath,
		Status:        status,
		RequestTime:   requestTime,
		IsErrorStatus: isErrorStatus,
	}

	return logEntry, nil
}
